<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <title>WebTerminal</title>
    <!--引入css-->
    <!--Open Sans Font-->
    <link href='https://fonts.googleapis.com/css?family=Open+Sans:400,300,600,700' rel='stylesheet' type='text/css'>
    <!--Bootstrap Stylesheet [ REQUIRED ]-->
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
    <!--Nifty Stylesheet [ REQUIRED ]-->
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/nifty.css') }}">
    <!--FontAwesome [ DEMONSTRATION ]-->
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/font-awesome.min.css') }}">

    <!--Select2-->
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='plugins/select2/css/select2.min.css') }}">
    <!--zTree-->
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='plugins/zTree/css/awesomeStyle/awesome.css') }}">
    <!--xterm-->
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='plugins/xterm/xterm.css') }}">

    <style>
        body {
            margin: 0;
            padding: 0;
            font-size: 12px;
            font-weight: normal;
        }

        .header {
            height: 35px;
            width: 100%;
            background-color: #3B4047;
            position: fixed;
            line-height: 35px;
            color: #fff;
            font-size: 18px;
            padding-left: 15px;
            font-weight: 800;
            z-index: 100;
        }

        .lefter {
            position: fixed;
            padding-left: 10px;
            padding-top: 5px;
            top: 35px;
            height: 100%;
            width: 200px;
            background-color: #535E64;
            color: #fff;
            z-index: 98;
            box-shadow: 0 1px 8px rgba(0, 0, 0, 0.6);
        }

        .lefter.in {
            width: 0;
            padding-left: 0;
        }

        .lefter.out {
            width: 200px;
            padding-left: 10px;
        }

        .bars {
            position: absolute;
            top: 42%;
            right: -16px;
        }

        .bars .full-screen {
            display: block;
            position: relative;
            cursor: pointer;
            width: 16px;
            height: 32px;
            background-color: #5cb85c;
            border-radius: 0 16px 16px 0;
        }

        .bars .full-screen .open, .full-screen .close {
            display: inline-block;
            color: #fff;
            font-size: 16px;
            opacity: 1;
            text-shadow: none;
            font-weight: 400;
            position: absolute;
            top: 7px;
            left: 3px;
        }

        .bars .full-screen .open {
            display: none;
        }

        .righter {
            position: absolute;
            overflow: auto;
            padding-left: 200px;
            padding-top: 35px;
            background-color: #000;
            color: #fff;
            width: 100%;
            height: 100%;
        }

        .righter.in {
            padding-left: 0;
        }

        .righter.out {
            padding-left: 200px;
        }

        /* 宽度小于768px */
        @media screen and (max-width: 768px) {

            .lefter {
                box-shadow: 1px 1px 3px #FFF;
            }

            .righter {
                padding-left: 0;
            }

            .righter.in {
                padding-left: 0;
            }

            .righter.out {
                padding-left: 0;
            }
        }


        /* ztree样式 */

        .ztree,
        .ztree *
        {
            font-size: 13px;
            background-color: #535E64;
            color: #fff;
        }

        .ztree li span.button::before,
        .ztree li span.button.ico_close::before,
        .ztree li span.button.ico_open::before,
        .ztree li span.button.ico_docu::before {
            color: #fff;
        }

        .ztree li span.button.ico_docu::before {
            content: "\f007";
        }

        .ztree li a.curSelectedNode {
          background-color: #41494E;
        }

        .ztree li a:hover {
          background: #3C4448;
        }


        /*tab样式修改*/

        .tab-base {
            margin-bottom: 0!important;
        }

        .nav-tabs {
            background-color: #DEE1E6;
        }

        .nav-tabs>li {
            margin-bottom: 0 !important;
        }

        .nav-tabs>li>a {
            border: none;
        }

        .tab-base .nav-tabs > .active > a,
        .tab-base .nav-tabs > .active a:hover,
        .tab-base .nav-tabs > .active > a:focus {
            border: 0;
            background-color: #ffffff;
        }

        .tab-base .nav-tabs > li:not(.active) > a {
            background-color: #DEE1E6;
            transition: background-color, 0.3s;
        }

        .tab-base .nav-tabs > li > a.tab {
            position: relative !important;
            margin-right: 1px !important;
            padding: 9px !important;
            padding-right: 30px !important;
            border-radius: 2px;
        }


        .tab-base .tab-content {
            background-color: #000 !important;
            color: #fff !important;
            padding: 0 !important;
        }

        .close{
            position: absolute;
            right: 2px;
            top: 5px;
        }
    </style>

</head>
<body>

<div class="header">
    SSH
</div>

<div class="lefter">
    <ul id="tree" class="ztree"></ul>
    <div class="bars">
        <span class="full-screen">
            <b class="open fa fa-angle-right" title="展开"></b>
            <b class="close fa fa-angle-left" title="关闭"></b>
        </span>
    </div>
</div>

<div class="righter">
    <div class="tab-base">
        <!--Nav Tabs-->
        <ul class="nav nav-tabs">
        </ul>

        <!--Tabs Content-->
        <div class="tab-content">
        </div>
    </div>
</div>


</body>

<!--引入JavaScript-->
<!--jQuery [ REQUIRED ]-->
<script src="{{ url_for('static', filename='js/jquery.min.js') }}"></script>
<!--BootstrapJS [ RECOMMENDED ]-->
<script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
<!--NiftyJS [ RECOMMENDED ]-->
<script src="{{ url_for('static', filename='js/nifty.js') }}"></script>
<!--CsrfJS-->
<script src="{{ url_for('static', filename='js/csrf.js') }}"></script>
<!--Select2-->
<script src="{{ url_for('static', filename='plugins/select2/js/select2.full.min.js') }}"></script>
<script src="{{ url_for('static', filename='plugins/select2/js/i18n/zh-CN.js') }}"></script>
<!--zTree-->
<script src="{{ url_for('static', filename='plugins/zTree/js/jquery.ztree.all.js') }}"></script>
<!--xterm-->
<script type="text/javascript" src="{{ url_for('static', filename='plugins/xterm/xterm.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='plugins/xterm/addons/fit/fit.js') }}"></script>
<!--socket-io-->
<script type="text/javascript" src="{{ url_for('static', filename='js/socket.io.js') }}"></script>

<script type="text/javascript">

    var queues = [];

    var identifyMapping = {
    };

    var socketsMapping = {
    };

    var termsMapping = {};

    var chars = ['0','1','2','3','4','5','6','7','8','9',
        'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
    function generateMixed(n) {
         var res = "";
         for(var i = 0; i < n ; i ++) {
             var id = Math.ceil(Math.random()*35);
             res += chars[id];
         }
         return res;
    }

    function getActiveTabTitle() {
        return $('.nav-tabs li.active').text().trim();
    }

    function addTab(title, identify) {
        var existTabIdentify = identifyMapping[title];
        // 如果存在就直接激活当前窗口
        if (existTabIdentify){
            $(".nav-tabs").children('li').removeClass('active');
            $(".tab-content").children('div').removeClass('active').removeClass('in');

            $('.nav-tabs a[href="#tab-'+ existTabIdentify +'"]').closest('li').addClass('active');
            $('.tab-content div[id="tab-'+ existTabIdentify +'"]').addClass('active').addClass('in');
            return true;
        // 如果不存在就增加并且激活当前窗口
        }else {
            var tab =
            '<li class="active"><a class="tab" data-toggle="tab" href="#tab-'+identify+'">' +
                '<span>'+title+'</span> ' +
                '<button class="close" data-dismiss="alert"><i class="pci-cross"></i></button></a> ' +
            '</li>';
            var content =
                '<div id="tab-'+identify+'" class="tab-pane fade active in">' +
                    '<div class="screen"> ' +
                        '<div id="terms-'+identify+'"></div> ' +
                    '</div>' +
                '</div>';
            $(".nav-tabs").children('li').removeClass('active');
            $(".nav-tabs").append(tab);

            $(".tab-content").children('div').removeClass('active').removeClass('in');
            $(".tab-content").append(content);
            identifyMapping[title] = identify;
            queues.push(title);
            return false
        }
    }

    function deleteTab(title) {
        // active other tab
        var activeTitle = getActiveTabTitle();
        var currentIndex = queues.indexOf(title);
        if(title == activeTitle){   //当前激活的窗口被删除，显示前面的窗口
            if(queues.length > 1){
                var beforeIndex = currentIndex - 1;
                if(beforeIndex < 0){
                    beforeIndex = currentIndex + 1;
                }
                var beforeTitle = queues[beforeIndex];
                var beforeIdentify = identifyMapping[beforeTitle];
                $('.nav-tabs a[href="#tab-'+ beforeIdentify +'"]').closest('li').addClass('active');
                $('.tab-content div[id="tab-'+ beforeIdentify +'"]').addClass('active').addClass('in');
                queues.splice(currentIndex, 1);
                console.log('queues-->', queues);
            }
        }else {
            queues.splice(currentIndex, 1);
            console.log('queues==>', queues)
        }

        // delete tab
        var identify = identifyMapping[title];
        delete identifyMapping[title];
        $(".nav-tabs").find('a[href="#tab-'+identify+'"]').closest('li').remove();
        $(".tab-content").find('div[id="tab-'+identify+'"]').remove();

        // close socket
        var sock = socketsMapping[title];
        sock.close();
        delete socketsMapping[title];
    }

    function setTermSize(ele) {
        var headerHeight = $(".header").height();
        var navTabHeight = $(".nav-tabs").height();
        $(ele).css("height", document.documentElement.clientHeight - headerHeight - navTabHeight)
            .css('box-sizing', 'border-box')
            .css("padding-left", '5px')
            .css("padding-top", '5px');
    }

    var url = window.location.protocol === "https:" ? "wss://" : "ws://" + document.domain + ':' + location.port;
    function initXterm(asset, system_user) {
        var term;
        var room;
        var secret = generateMixed(16);
        var title = system_user.name +'@'+ asset.name;
        var closed = false;

        if (addTab(title, secret)){
            return
        }

        var socket = io.connect(url + '/ssh');
        socketsMapping[title] = socket;

        socket.on('connect', function () {
            setTermSize("#terms-"+secret+"");
            Terminal.applyAddon(fit);  // 自适应大小
            term = new Terminal({
                screenKeys: true,
                useStyle: true,
                cursorBlink: true, //光标闪烁
                //cursorStyle: 'underline', //光标样式
                theme: {
                    foreground: '#fff',   //字体
                    background: '#000' //背景色
                },
                fontFamily: '"Monaco", "Consolas", "monospace"',
                fontSize: 16
            });
            term.open(document.getElementById("terms-"+secret+""));
            term.fit();
            term.clear();
            termsMapping[title] = term;

            term.on('data', function (data) {
                socket.emit('data', {data: data, room: room});
            });

            var cols = term.proposeGeometry().cols;
            var rows = term.proposeGeometry().rows;
            socket.emit('host', {
                asset_id: asset.id ,
                system_user_id: system_user.id,
                secret: secret,
                size: [cols, rows]
            });
        });

        socket.on('disconnect', function () {
            console.log('disconnect......', closed);
            if(!closed){
                socket.close();
                term.write('\r\nlogout');
            }
        });

        socket.on('data', function (message) {
            term.write(message.data);
            if(message.data == '\r\nlogout'){
                console.log('close......');
                closed = true;
                socket.close();
            }
        });

        socket.on('room', function (message) {
            console.log('room......') ;
            if (message.secret == secret) {
                room = message.room;
            }
        });

        $("#terms-"+secret+"").on('fullScreen.change', function () {
            term.fit();
        }).on('nav.tab.change', function () {
            setTermSize("#terms-"+secret+"");
            term.fit();
        });
    }

    function filter(treeId, parentNode, childNodes) {
        if (!childNodes) return null;
        for (var i = 0, l = childNodes.length; i < l; i++) {
            childNodes[i].name = childNodes[i].name.replace(/\.n/g, '.');
        }
        return childNodes;
    }

    function beforeClick(treeId, treeNode) {
        // 打开新的窗口
        if (treeNode.isParent == false) {
            var parent = treeNode.getParentNode();
            var asset = {
                id: parent.id,
                name: parent.name
            };
            var system_user = {
                id: treeNode.id,
                name: treeNode.name
            };
            initXterm(asset, system_user)
        }
    }

    function initTree() {
        var setting = {
            view: {
                showLine: true
            },
            async: {
                enable: true,
                url: "{{ url_for('terminal.ssh-tree') }}",
                autoParam: ["id", "name=n", "level=lv"],
                dataFilter: filter
            },
            callback: {
                beforeClick: beforeClick
            }
        };
        $.fn.zTree.init($("#tree"), setting);
    }

    $(document).ready(function () {
        initTree();

        // nav-tabs事件
        $(".nav-tabs").on('click', '.close', function (e) {
            var title = $(e.target).closest('.tab').children('span').text();
            deleteTab(title);
        }).on('shown.bs.tab', 'a[data-toggle="tab"]', function (e) {
            if($('.nav-tabs li.active').length) {
                var identify = $('.nav-tabs li.active').children('a.tab').attr('href').split('-')[1];
                $("#terms-" + identify + "").trigger('nav.tab.change');
            }
        });

        // 满屏切换样式
        $(".full-screen").on('click', function () {
            if($(".full-screen .open").css('display') == 'none') {
                $(".full-screen .open").css('display', "block");
                $(".full-screen .close").css('display', "none");
                $(".lefter").removeClass('out').addClass('in').children('.ztree').hide();
                $(".righter").removeClass('out').addClass('in');
            }else {
                $(".full-screen .open").css('display',"none");
                $(".full-screen .close").css('display',"block");
                $(".lefter").removeClass('in').addClass('out').children('.ztree').show();
                $(".righter").removeClass('in').addClass('out');
            }

            //获取当前激活的term窗口
            if($('.nav-tabs li.active').length){
                var identify = $('.nav-tabs li.active').children('a.tab').attr('href').split('-')[1];
                $("#terms-"+identify+"").trigger('fullScreen.change');
            }
        });

    });

</script>

</html>
